package cn.mall.framework.common;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import lombok.extern.slf4j.Slf4j;
import cn.mall.framework.biz.bean.BusinessException;
import cn.mall.framework.biz.bean.BusinessRequestOrResponse;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

@Slf4j
public class FrameworkKit {
	
	public static Map<String, Object> parseResponseToMap(Object responseObject)
			throws BusinessException {
		Map<String, Object> responseMap = new HashMap<String, Object>();
		if (responseObject == null)
			return responseMap;
		for (Class<?> clazz = responseObject.getClass(); !clazz.getName().equals(
				Object.class.getName()); clazz = clazz.getSuperclass()) {
			Field[] fields = clazz.getDeclaredFields();
			for (Field field : fields) {
				String fieldName = field.getName();
				String key = fieldName;
				String format = null;
				if (field.isAnnotationPresent(Param.class)) {
					Param param = field
							.getAnnotation(Param.class);
					String markKey = param.key();
					if (markKey != null && !markKey.equals("")) {
						key = markKey;
					}
					format = param.format();
				}
				Object ret = null;
				try {
					ret = getFieldValueByTypeAndFormat(field,
							responseObject, format);
				} catch (Exception e) {
					log.error("", e);
				}
				if (ret != null)
					responseMap.put(key, ret);
			}
		}
		return responseMap;
	}

	public static Map<String, Object> parseResponseToMapWithSubResponse(Object responseObject)
			throws BusinessException {
		Map<String, Object> responseMap = new HashMap<String, Object>();
		BeanToMap(responseObject, responseMap);
		return responseMap;
	}

	@SuppressWarnings("unchecked")
	private static void BeanToMap(Object responseObject, Map<String, Object> responseMap)
			throws BusinessException {
		for (Class<?> clazz = responseObject.getClass(); !clazz.getName().equals(
				Object.class.getName()); clazz = clazz.getSuperclass()) {
			Field[] fields = clazz.getDeclaredFields();
			for (Field field : fields) {
				field.setAccessible(true);
				Type fieldType = field.getGenericType();
				boolean listFieldGenericIsMap = false;
				if (fieldType instanceof ParameterizedType) {
					ParameterizedType paraType = (ParameterizedType) fieldType;
					Type t = paraType.getActualTypeArguments()[0];
					if (t instanceof ParameterizedType) {
						ParameterizedType tType = (ParameterizedType) t;
						if(tType.getRawType() == Map.class){
							listFieldGenericIsMap = true;
						}
					}
				}
				if (BusinessRequestOrResponse.class.isAssignableFrom(field
						.getType())) {
					try {
						Object subObj = field.get(responseObject);
						if (subObj == null) {
							subObj = field.getType().newInstance();
							field.set(responseObject, subObj);
						}
						BeanToMap(subObj, responseMap);
					} catch (Exception e) {
						log.error("", e);
					}
				} else if(!listFieldGenericIsMap && List.class.isAssignableFrom(field.getType())){
					try {
						List<Map<String,Object>> list = (List<Map<String,Object>>)field.get(responseObject);
						if(list==null) continue;
						
						List<Map<String,Object>> retList = new ArrayList<Map<String,Object>>();
						for(int i=0;i<list.size();i++){
							Map<String,Object> childMap = new HashMap<String,Object>();
							BeanToMap(list.get(i), childMap);
							retList.add(childMap);
						}
						responseMap.put(field.getName(), retList);
					} catch (Exception e) {
						log.error("",e);
					}
				} else {
					String fieldName = field.getName();
					String key = fieldName;
					String format = null;
					if (field.isAnnotationPresent(Param.class)) {
						Param param = field
								.getAnnotation(Param.class);
						String markKey = param.key();
						if (markKey != null && !markKey.equals("")) {
							key = markKey;
						}
						format = param.format();
					}
					Object ret = null;
					try {
						ret = getFieldValueByTypeAndFormat(field,
								responseObject, format);
					} catch (Exception e) {
						log.error("", e);
					}
					if (ret != null)
						responseMap.put(key, ret);
				}
			}
		}
	}
	
	public static Object getFieldValueByTypeAndFormat(Field field, Object bean,
			String format) throws ParseException, IllegalArgumentException,
			IllegalAccessException {
		field.setAccessible(true);
		Object objValue = field.get(bean);
		Object ret = null;
		if (objValue == null)
			return ret;
		String type = field.getType().getName();
		if ("java.lang.String".equals(type)) {
			ret = field.get(bean).toString().trim();
		} else if ("java.lang.Integer".equals(type) || "int".equals(type)) {
			if (objValue instanceof Integer) {
				ret = ((Integer) objValue).toString();
			}
		} else if ("java.lang.Float".equals(type) || "float".equals(type)) {
			if (objValue instanceof Float) {
				ret = ((Float) objValue).toString();
			}
		} else if ("java.lang.Double".equals(type) || "double".equals(type)) {
			if (objValue instanceof Double) {
				ret = ((Double) objValue).toString();
			}
		} else if ("java.math.BigDecimal".equals(type)) {
			if (objValue instanceof BigDecimal) {
				ret = ((BigDecimal) objValue).toString();
			}
		} else if ("java.util.Date".equals(type)) {
			if (objValue instanceof Date) {
				Date dateValue = (Date) objValue;
				ret = new SimpleDateFormat(format).format(dateValue);
			}
		} else if ("java.lang.Boolean".equals(type)) {
			if (objValue instanceof Boolean) {
				ret = ((Boolean) objValue).toString();
			}
		} else if ("java.lang.Long".equals(type) || "long".equals(type)) {
			if (objValue instanceof Long) {
				ret = ((Long) objValue).toString();
			}
		} else {
			ret = objValue;
		}
		return ret;
	}
	
	public static void setFieldValueByType(Field field, Object bean,
			String value) throws IllegalArgumentException,
			IllegalAccessException, ParseException, ClassNotFoundException {
		setFieldValueByTypeAndFormat(field,bean,value,null);
	}

	@SuppressWarnings("unchecked")
	public static void setFieldValueByTypeAndFormat(Field field, Object bean,
			String value, String format) throws IllegalArgumentException,
			IllegalAccessException, ParseException, ClassNotFoundException {
		if (value == null)
			return;
		field.setAccessible(true);
		String type = field.getType().getName();
		if ("java.lang.String".equals(type)) {
			field.set(bean, value);
		} else if ("java.lang.Integer".equals(type)) {
			field.set(bean, Integer.valueOf(value));
		} else if ("int".equals(type)) {
			field.set(bean, Integer.valueOf(value).intValue());
		} else if ("java.lang.Float".equals(type)) {
			field.set(bean, Float.valueOf(value));
		} else if ("float".equals(type)) {
			field.set(bean, Float.valueOf(value).floatValue());
		} else if ("java.lang.Double".equals(type)) {
			field.set(bean, Double.valueOf(value));
		} else if ("double".equals(type)) {
			field.set(bean, Double.valueOf(value).doubleValue());
		} else if ("java.math.BigDecimal".equals(type)) {
			field.set(bean, new BigDecimal(value));
		} else if ("java.util.Date".equals(type)) {
			Date date = new SimpleDateFormat(format).parse(value.toString());
			field.set(bean, date);
		} else if ("java.lang.Boolean".equals(type)) {
			field.set(bean, Boolean.valueOf(value));
		} else if ("java.lang.Long".equals(type)) {
			field.set(bean, Long.valueOf(value));
		} else if ("long".equals(type)) {
			field.set(bean, Long.valueOf(value).longValue());
		} else if ("java.util.Map".equals(type)) {
			JSONObject mapObj = JSONObject.fromObject(value);
			field.set(bean,mapObj);
		} else if ("java.util.List".equals(type)) {
			JSONArray arr = JSONArray.fromObject(value);
			ParameterizedType genericType = (ParameterizedType) field.getGenericType();
			Type listType = genericType.getActualTypeArguments()[0];
			if (listType instanceof ParameterizedType) {
				ParameterizedType paraType = (ParameterizedType) listType;
				if ("java.util.Map".equals(((Class<?>)paraType.getRawType()).getName())) {
					List<Map<String, String>> list = new ArrayList<Map<String, String>>();
					for (int i = 0; i < arr.size(); i++) {
						Map<String,String> classMap = new HashMap<String,String>();
						Map<String, String> map = (Map<String, String>) JSONObject
								.toBean(JSONObject.fromObject(arr.optString(i)),
										Map.class, classMap);
						list.add(map);
					}
					field.set(bean, list);
				}
			} else if(List.class.isAssignableFrom(field.getType())){
				String argCalssName = listType.toString().substring(listType.toString().lastIndexOf(" ") + 1);
				Class<?> clazz = Class.forName(argCalssName);
				List<Object> list = new ArrayList<Object>();
				for (int index = 0; index < arr.size(); index++) {
					Object element = arr.get(index);
					if (clazz == element.getClass()) {
						list.add(element);
					} else {
						Object obj = JSONObject.toBean(JSONObject.fromObject(arr.optString(index)), clazz);
						list.add(obj);
					}
				}
				field.set(bean, list);
			} else {
				field.set(bean, arr);
			}
		}

	}

	public static Field getFieldByName(Object obj, String key) {
		if (obj == null || key == null)
			return null;
		for (Class<?> clazz = obj.getClass(); !clazz.getName().equals(Object.class.getName()); clazz = clazz
				.getSuperclass()) {
			Field[] fields = clazz.getDeclaredFields();
			for (Field f : fields) {
				if (f.getName().equals(key)) {
					return f;
				}
			}
		}
		return null;

	}
	
	public static Field getFieldByNameIgnoreCase(Class<?> clazz, String key) {
		if (clazz == null || key == null)
			return null;
		for (; !clazz.getName().equals(Object.class.getName()); clazz = clazz
				.getSuperclass()) {
			Field[] fields = clazz.getDeclaredFields();
			for (Field f : fields) {
				if (f.getName().equalsIgnoreCase(key)) {
					return f;
				}
			}
		}
		return null;

	}
	
	public static Object getFieldValueByNameIgnoreCase(Object obj, String key) {
		if (obj == null || key == null)
			return null;
		Class<? extends Object> clazz = obj.getClass();
		for (; !clazz.getName().equals(Object.class.getName()); clazz = clazz
				.getSuperclass()) {
			Field[] fields = clazz.getDeclaredFields();
			for (Field f : fields) {
				if (f.getName().equalsIgnoreCase(key)) {
					f.setAccessible(true);
					try {
						return f.get(obj);
					} catch (Exception e) {
						
					}
				}
			}
		}
		return null;
	}
	
}
